home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1997 January / macformat46.iso / Shareware Plus / Developers / Library / Grant's CGI Framework / Grant's CGI Framework / grantscgi / Util / ProcessUtil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-23  |  20.6 KB  |  856 lines

  1. /*****
  2.  *
  3.  *    ProcessUtil.c
  4.  *
  5.  *    This is a support file for "Grant's CGI Framework".
  6.  *    Please see the license agreement that accompanies the distribution package
  7.  *    for licensing details.
  8.  *
  9.  *    Copyright ©1995,1996 by Grant Neufeld
  10.  *    grant@acm.com
  11.  *    http://arpp.carleton.ca/cgi/framework/
  12.  *
  13.  *****/
  14.  
  15. #include "MyConfiguration.h"
  16.  
  17. #include <Threads.h>
  18. #if kCompilingForWSAPI
  19. #include <WSAPI.h>
  20. #endif
  21.  
  22. #include "globals.h"
  23.  
  24. #include "CustomHandlers.h"
  25. #include "DebugUtil.h"
  26. #include "PrefUtil.h"
  27.  
  28. #include "ProcessUtil.h"
  29.  
  30.  
  31. /***  LOCAL VARIABLES  ***/
  32.  
  33. static short    vAppBusyLevel;        /* how busy the application is */
  34. static UInt32    vSleepTicksDefault;    /* sleep time when app is idle */
  35. static UInt32    vSleepTicksBusy;    /* sleep time when app is processing */
  36.  
  37. static ThreadID    vThreadMain;        /* main application thread id */
  38. static Boolean    vThreadMainIsAsleep;
  39.  
  40. #if kCompileWithDeferredTask
  41. static ThreadID    vDeferredTaskThread;
  42. #endif
  43. //static ThreadID    vThreadStatusUpdate;
  44.  
  45. /* an array of sleeping thread IDs */
  46. #if kStartupThreadsPreallocate > nil
  47. static ThreadID    vThreadSleepers[kStartupThreadsPreallocate]; /* array of sleepers */
  48. static ThreadID vThreadSleeperHead;        /* next thread to wake up */
  49. static ThreadID vThreadSleeperAddHere;    /* next open spot in queue */
  50. static short    vThreadSleepersTotal;    
  51. #endif
  52.  
  53.  
  54. /***  LOCAL PROTOTYPES  ***/
  55.  
  56. pascal void        threadTermination            ( ThreadID, void * );
  57. pascal void *    threadDeferredTaskThread    ( void * );
  58. static void        threadSleepersInit            ( void );
  59.  
  60.  
  61. /***  FUNCTIONS  ***/
  62.  
  63. /*  */
  64. void
  65. ProcessStartup ( void )
  66. {
  67.     Handle    thePref;
  68.     
  69.     thePref = PrefItemGet ( krPrefSleepTicks, kPrefResType );
  70.     if ( thePref != NULL )
  71.     {
  72.         ProcessSleepSetDefault ( **((UInt32 **)thePref) );
  73.         PrefItemRelease ( thePref );
  74.     }
  75.     else
  76.     {
  77.         ProcessSleepSetDefault ( kSleepTicks );
  78.     }
  79.     
  80.     thePref = PrefItemGet ( krPrefSleepTicksBusy, kPrefResType );
  81.     if ( thePref != NULL )
  82.     {
  83.         ProcessSleepSetBusy ( **((UInt32 **)thePref) );
  84.         PrefItemRelease ( thePref );
  85.     }
  86.     else
  87.     {
  88.         ProcessSleepSetBusy ( kSleepTicksWhenBusy );
  89.     }
  90.     
  91.     ProcessSleepDefault    ();
  92.     
  93.     /* we're not busy yet */
  94.     vAppBusyLevel = nil;
  95.     
  96.     gFrontProcess = ProcessCurrentIsFront ();
  97.     
  98.     #if kCompileWithProcessFileSpec
  99.     ProcessGetMyFSSpec ( &gProcessFSSpec );
  100.     #endif
  101. } /* ProcessStartup */
  102.  
  103.  
  104. /* Give time for other processes (both internal to the app and other applications)
  105.     sleepTicks is the suggested amount of time to give. if nil, default will be used.
  106.     useWNE determines if time will be given to other applications (via WNE),
  107.         or just to other threads in the current application.
  108.     theCGIHdl can be NULL if you're not calling from a CGI function. */
  109. p_export
  110. void
  111. ProcessGiveTime ( UInt32 sleepTicks, Boolean useWNE, CGIHdl theCGIHdl )
  112. {
  113.     #if kCompilingForWSAPI
  114.     #pragma unused(useWNE)
  115.     
  116.     my_assert ( theCGIHdl != NULL, "\pProcessGiveTime: theCGIHdl is NULL" );
  117.     
  118.     WSAPI_YieldTime ( (*theCGIHdl)->wsapi, sleepTicks );
  119.     
  120.     #else
  121.     #pragma unused(theCGIHdl)
  122.     
  123.     OSErr            theErr;
  124.     #if kCompileWithThreadsOptional
  125.     EventRecord        theEvent;
  126.     
  127.     if ( gHasThreadMgr )
  128.     {
  129.     #endif
  130.         
  131.         theErr = ThreadYield ( nil, useWNE );
  132.         
  133.     #if kCompileWithThreadsOptional
  134.     }
  135.     else if ( useWNE )
  136.     {
  137.         if ( sleepTicks > nil )
  138.         {
  139.             /* if sleepTicks is a 'usable' value, use it */
  140.             WaitNextEvent ( nil, &theEvent, sleepTicks, NULL );
  141.         }
  142.         else
  143.         {
  144.             /* otherwise, use the current application sleep ticks setting */
  145.             WaitNextEvent ( nil, &theEvent, gSleepTicks, NULL );
  146.         }
  147.     }
  148.     #else
  149.     #pragma unused(sleepTicks)
  150.     #endif
  151.     
  152.     #endif /* kCompilingForWSAPI */
  153. } /* ProcessGiveTime */
  154.  
  155.  
  156. /* Determine if the current application is active (is in front of all others) */
  157. Boolean
  158. ProcessCurrentIsFront ( void )
  159. {
  160.     #if kCompileWithForeground
  161.     
  162.     Boolean                    isFront;
  163.     ProcessSerialNumber        myPSN;
  164.     OSErr                    theErr;
  165.     
  166.     /* get my process serial number. IM:Processes 2-21 */
  167.     theErr = GetCurrentProcess ( &myPSN );
  168.     if ( theErr != noErr )
  169.     {
  170.         /* might want to do something else here to handle the error */
  171.         return false;
  172.     }
  173.     
  174.     isFront = ProcessIsFront ( &myPSN );
  175.     
  176.     return isFront;
  177.     
  178.     #else    /* if not kCompileWithForeground */
  179.     
  180.     return false;
  181.     
  182.     #endif    /* kCompileWithForeground */
  183. } /* ProcessCurrentIsFront */
  184.  
  185.  
  186. /* Run through WaitNextEvent, not handling events, until the process comes
  187.     to the foreground.
  188.     Returns false if timeout is reached before process brought to front.
  189.     Timeout is in ticks (60ths of a second). If timeout is nil - doesn't timeout. */
  190. Boolean
  191. ProcessWaitUntilFront ( unsigned long timeout )
  192. {
  193.     #if kCompileWithForeground
  194.     Boolean            isFront;
  195.     long            endTime;
  196.     long            sleepTicks;
  197.     
  198.     isFront = ProcessCurrentIsFront ();
  199.     if ( !isFront )
  200.     {
  201.         endTime = TickCount() + timeout;
  202.         
  203.         if ( (timeout > 20) || (timeout == nil) )
  204.         {
  205.             sleepTicks = 20;
  206.         }
  207.         else
  208.         {
  209.             sleepTicks = timeout;
  210.         }
  211.         
  212.         do
  213.         {
  214.             ProcessGiveTime ( sleepTicks, true, NULL );
  215.             
  216.             isFront = ProcessCurrentIsFront ();
  217.         } while ( !isFront && ((timeout == nil) || (endTime >= TickCount())) );
  218.     }
  219.     
  220.     return isFront;
  221.     
  222.     #else
  223.         /* can't be in foreground/front if application is background only */
  224.         return false;
  225.     #endif /* kCompileWithForeground */
  226. } /* ProcessWaitUntilFront */
  227.  
  228.  
  229. /* Determine if the specified application is active (is in front of all others) */
  230. Boolean
  231. ProcessIsFront ( ProcessSerialNumber *thePSN )
  232. {
  233.     #if kCompileWithForeground
  234.     OSErr                    theErr;
  235.     ProcessSerialNumber        frontPSN;
  236.     
  237.     my_assert ( thePSN != NULL, "\pProcessIsFront: thePSN ptr is NULL" );
  238.     
  239.     theErr = GetFrontProcess ( &frontPSN );
  240.     if ( theErr != noErr )
  241.     {
  242.         /* might want to do something else here to handle the error */
  243.         return false;
  244.     }
  245.     
  246.     return ( (frontPSN.lowLongOfPSN == thePSN->lowLongOfPSN) &&
  247.         (frontPSN.highLongOfPSN == thePSN->highLongOfPSN) );
  248.     
  249.     #else
  250.         /* can't be in foreground/front if application is background only */
  251.         return false;
  252.     #endif /* kCompileWithForeground */
  253. } /* ProcessIsFront */
  254.  
  255.  
  256. /* Fill out an already allocated FSSpec with the current process' location.
  257.     Thanks to Gregory S. Combs for providing most of this function. */
  258. OSErr
  259. ProcessGetMyFSSpec ( FSSpec *procFileSpec )
  260. {
  261.     OSErr                    theErr;
  262.     ProcessSerialNumber        curPSN;
  263.     ProcessInfoRec            procInfo;
  264.     Str255                    appName;
  265.     
  266.     my_assert ( procFileSpec != NULL, "\pProcessGetMyFSSpec: procFileSpec ptr is NULL" );
  267.     
  268.     theErr = GetCurrentProcess ( &curPSN );
  269.     if ( theErr == noErr )
  270.     {
  271.         procInfo.processInfoLength    = sizeof ( ProcessInfoRec );
  272.         procInfo.processName        = appName;
  273.         procInfo.processAppSpec        = procFileSpec;
  274.         
  275.         theErr = GetProcessInformation ( &curPSN, &procInfo );
  276.     }
  277.     
  278.     return theErr;
  279. } /* ProcessGetMyFSSpec */
  280.  
  281.  
  282. /**  BUSY LEVEL and SLEEP TICKS  **/
  283. #pragma mark -
  284.  
  285. /* The 'busy' level of the application affects how much time it will try to
  286.     grab for processing */
  287.  
  288. /*  */
  289. p_export
  290. void
  291. ProcessIsMoreBusy ( void )
  292. {
  293.     #if kCompileWithDeferredTask
  294.     OSErr    theErr;
  295.     #endif
  296.     
  297.     if ( vAppBusyLevel == nil )
  298.     {
  299.         /* we weren't busy, so need to move into busy state */
  300.         /* adjust the sleepTicks */
  301.         ProcessSleepBusy ();
  302.         /* put the deferredTasks thread to sleep */
  303.         #if kCompileWithDeferredTask
  304.         if ( vDeferredTaskThread != nil )
  305.         {
  306.             theErr = SetThreadState ( vDeferredTaskThread, kStoppedThreadState, nil );
  307.         }
  308.         #endif
  309.     }
  310.     vAppBusyLevel++;
  311. } /* ProcessIsMoreBusy */
  312.  
  313. /*  */
  314. p_export
  315. void
  316. ProcessIsLessBusy ( void )
  317. {
  318.     #if kCompileWithDeferredTask
  319.     OSErr    theErr;
  320.     #endif
  321.     
  322.     vAppBusyLevel--;
  323.     if ( vAppBusyLevel == nil )
  324.     {
  325.         /* we're no longer busy, so need to move into non-busy state */
  326.         /* adjust the sleepTicks */
  327.         ProcessSleepDefault ();
  328.         /* wake up the deferredTasks thread */
  329.         #if kCompileWithDeferredTask
  330.         if ( vDeferredTaskThread != nil )
  331.         {
  332.             theErr = SetThreadState ( vDeferredTaskThread, kReadyThreadState, nil );
  333.         }
  334.         #endif
  335.     }
  336. } /* ProcessIsLessBusy */
  337.  
  338.  
  339. /* This sets the sleepTicks to the default normal or busy level, as appropriate */
  340. void
  341. ProcessResetSleep ( void )
  342. {
  343.     if ( vAppBusyLevel > nil )
  344.     {
  345.         ProcessSleepBusy ();
  346.     }
  347.     else
  348.     {
  349.         ProcessSleepDefault ();
  350.     }    
  351. } /* ProcessResetSleep */
  352.  
  353.  
  354. /* sleepTicks is used to determine how much time to give for other processes
  355.     to use. */
  356.  
  357. /* set the number of ticks used for normal sleep time */
  358. p_export
  359. void
  360. ProcessSleepSetDefault ( UInt32 sleepTicks )
  361. {
  362.     vSleepTicksDefault = sleepTicks;
  363. } /* ProcessSleepSetDefault */
  364.  
  365. /* set the number of ticks used for busy sleep time */
  366. p_export
  367. void
  368. ProcessSleepSetBusy ( UInt32 sleepTicks )
  369. {
  370.     vSleepTicksBusy = sleepTicks;
  371. } /* ProcessSleepSetBusy */
  372.  
  373. /* get the number of ticks used for normal sleep time */
  374. p_export
  375. UInt32
  376. ProcessSleepGetDefault ( void )
  377. {
  378.     return vSleepTicksDefault;
  379. } /* ProcessSleepGetDefault */
  380.  
  381. /* get the number of ticks used for busy sleep time */
  382. p_export
  383. UInt32
  384. ProcessSleepGetBusy ( void )
  385. {
  386.     return vSleepTicksBusy;
  387. } /* ProcessSleepGetBusy */
  388.  
  389. /* Call this when app becmoes not busy */
  390. p_export
  391. void
  392. ProcessSleepDefault ( void )
  393. {
  394.     gSleepTicks = vSleepTicksDefault;
  395. } /* ProcessSleepDefault */
  396.  
  397. /* Call this when app becmoes busy */
  398. p_export
  399. void
  400. ProcessSleepBusy ( void )
  401. {
  402.     gSleepTicks = vSleepTicksBusy;
  403. } /* ProcessSleepBusy */
  404.  
  405.  
  406. /**  THREAD FUNCTIONS  **/
  407. #pragma mark -
  408.  
  409. void
  410. ThreadStartup ( void )
  411. {
  412.     OSErr    theErr;
  413.     long    feature;
  414.     
  415.     #if kCompileWithThreadsOptional
  416.     gHasThreadMgr = false;
  417.     #endif
  418.     
  419.     theErr = Gestalt ( gestaltThreadMgrAttr, &feature );
  420.     if ( theErr == noErr )
  421.     {
  422.         if (
  423.             #ifdef powerc
  424.             ( feature & (1 << gestaltThreadsLibraryPresent) ) &&
  425.             /* ••• Can anybody who does PowerPC code tell me why the following
  426.                 line doesn't work?
  427.                 ( (Ptr)NewThread != kUnresolvedSymbolAddress ) &&
  428.                 ••• */
  429.             #endif /* def powerc */
  430.             /* the feature check applies to both 68K & PPC */
  431.             ( feature & (1 << gestaltThreadMgrPresent) ) )
  432.         {
  433.             #if kCompileWithThreadsOptional
  434.             gHasThreadMgr = true;
  435.             #endif
  436.             
  437.             gThreadQuit = false;
  438.             
  439.             /* no sub threads running yet */
  440. //            #if kStartupThreadsPreallocate > nil
  441.             gThreadTotal = nil;
  442. //            /* no sleeping thread yet */
  443. //            gThreadSleeperIndex = 0;
  444. //            gThreadSleeper = nil;
  445. //            #endif
  446.             
  447.             /* determine the id of the main (current) thread */
  448.             GetCurrentThread ( &vThreadMain );
  449.             vThreadMainIsAsleep = false;
  450.             
  451.             #if (kStartupThreadsPreallocate > nil)
  452.             /* pre-allocate the required number of threads */
  453.             CreateThreadPool ( kCooperativeThread, kStartupThreadsPreallocate, nil );
  454.             
  455.             threadSleepersInit ();
  456.             #endif
  457.             
  458.             /* create the deferred tasks thread */
  459.             #if kCompileWithDeferredTask
  460.             theErr = ThreadNewThreadFromPool ( threadDeferredTaskThread,
  461.                 (void *)NULL, (void**)NULL, &vDeferredTaskThread );
  462.             #endif
  463.         }
  464.     }
  465. } /* ThreadStartup */
  466.  
  467.  
  468. /* you should always make sure that your sub-threads can't get 'stuck' in a loop.
  469.     They should, at the very least, check gThreadQuit periodically so that the
  470.     application can quit without getting hung on threads that won't go away. */
  471.  
  472.  
  473. /* Allocate a new thread from the existing pool of threads.
  474.     If there are no threads available, yield to other threads until one finishes.
  475.     The Thread Manager must be available for this function to work. */
  476. #if kStartupThreadsPreallocate > nil
  477. p_export
  478. OSErr
  479. ThreadNewThreadFromPool (
  480.     ThreadEntryProcPtr    threadEntry,
  481.     void *                threadParam,
  482.     void **                threadResult,
  483.     ThreadID *            threadMade )
  484. {
  485.     OSErr        theErr;
  486.     short        threadsFree;
  487.     ThreadID    currentThread;
  488.     
  489.     #if kCompileWithThreadsOptional
  490.     my_assert ( gHasThreadMgr, "\pThreadNewThreadFromPool: Thread Manager not available" );
  491.     #endif
  492.     
  493.     theErr = GetFreeThreadCount ( kCooperativeThread, &threadsFree );
  494.     if ( theErr == noErr )
  495.     {
  496.         theErr = GetCurrentThread ( ¤tThread );
  497.     }
  498.     
  499.     if ( (theErr == noErr) && (threadsFree == nil) )
  500.     {
  501.         /* Put the current thread to sleep, to be woken up when a thread
  502.             becomes available. */
  503.         theErr = ThreadSleep ( currentThread );
  504.     }
  505.     
  506.     if ( theErr == noErr )
  507.     {
  508.         /* Install the new thread using a premade thread from the pool. */
  509.         theErr = NewThread ( kCooperativeThread, threadEntry, threadParam, nil,
  510.             kFPUNotNeeded + kUsePremadeThread, threadResult, threadMade );
  511.     }
  512.     
  513.     if ( theErr == noErr )
  514.     {
  515.         /* set the termination function for the thread. */
  516.         SetThreadTerminator ( *threadMade, threadTermination, NULL );
  517.         
  518.         /* increment the total number of sub-threads. */
  519.         ++gThreadTotal;
  520.     }
  521.     
  522.     return theErr;
  523. } /* ThreadNewThreadFromPool */
  524. #endif
  525.  
  526.  
  527. /* The Thread Manager must be available for this function to work. */
  528. #if kStartupThreadsPreallocate > nil
  529. pascal void
  530. threadTermination ( ThreadID threadTerminated, void *terminationProcParam )
  531. {
  532. #pragma unused (threadTerminated,terminationProcParam)
  533.  
  534.     OSErr    theErr;
  535.     
  536.     #if kCompileWithThreadsOptional
  537.     my_assert ( gHasThreadMgr, "\pmyThreadTermination: Thread Manager not available" );
  538.     #endif
  539.     my_assert ( gThreadTotal > nil, "\pmyThreadTermination: gThreadTotal is too small" );
  540.     
  541.     /* if there are sleeping threads, wake one up since this one will be disposed */
  542.     theErr = ThreadWakeNext ();
  543.     
  544.     /* Lower the count of sub-threads. */
  545.     --gThreadTotal;
  546. } /* threadTermination */
  547. #endif
  548.  
  549.  
  550. /* Call this instead of YieldToAnyThread.
  551.     suggestThread is the suggested (but not required) thread to yield to next. */
  552. p_export
  553. OSErr
  554. ThreadYield ( ThreadID suggestThread, Boolean useWNE )
  555. {
  556. #pragma unused(suggestThread)
  557. #if kStartupThreadsPreallocate <= nil
  558. #pragma unused(useWNE)
  559. #endif
  560.     
  561.     OSErr        theErr;
  562.     #if kStartupThreadsPreallocate > nil
  563.     EventRecord    theEvent;
  564.     #endif
  565.     
  566.     #if kCompileWithThreadsOptional
  567.     my_assert ( gHasThreadMgr, "\pThreadYield: Thread Manager not available" );
  568.     #endif
  569.     
  570.     theErr = YieldToAnyThread ();
  571.     
  572.     /* if there's a sleeping thread, it's probably the main thread, so we'll
  573.         need to give time to other apps to process */
  574.     #if kStartupThreadsPreallocate > nil
  575.     if ( useWNE && (vThreadSleepersTotal > nil) )
  576.     {
  577.         WaitNextEvent ( nil, &theEvent, gSleepTicks, NULL );
  578.     }
  579.     #endif
  580.     
  581.     return theErr;
  582. } /* ThreadYield */
  583.  
  584.  
  585. /* Give all sub-threads a chance to finish */
  586. #if (kStartupThreadsPreallocate > nil)
  587. void
  588. ThreadFinishAllSubThreads ( void )
  589. {
  590.     OSErr        theErr;
  591.     ThreadID    currentThread;
  592.     
  593.     #if kCompileWithThreadsOptional
  594.     my_assert ( gHasThreadMgr, "\pThreadFinishAllSubThreads: Thread Manager not available" );
  595.     #endif
  596.     
  597.     theErr = GetCurrentThread ( ¤tThread );
  598.     if ( currentThread != vThreadMain )
  599.     {
  600.         /* lower the count of sub-threads to prevent this sub-thread from
  601.             preventing the exit of the while loop below */
  602.         --gThreadTotal;
  603.     }
  604.     
  605.     gThreadQuit = true;
  606.     
  607.     while ( gThreadTotal > nil )
  608.     {
  609.         if ( gThreadTotal <= vThreadSleepersTotal )
  610.         {
  611.             /* remaining threads are sleeping, so wake one up */
  612.             ThreadWakeNext ();
  613.         }
  614.         
  615.         /* while there remain other threads (not including the current and main),
  616.             let them finish before quitting */
  617.         ProcessGiveTime ( nil, true, NULL );
  618.     }
  619.     
  620.     if ( currentThread != vThreadMain )
  621.     {
  622.         /* increase the count of sub-threads */
  623.         ++gThreadTotal;
  624.     }
  625. } /* ThreadFinishAllSubThreads */
  626. #endif
  627.  
  628.  
  629. /**  Periodic Tasks  **/
  630. #pragma mark -
  631. #if kCompileWithPeriodicTask
  632.  
  633. /* ••• */
  634. void
  635. ProcessPeriodicTask ( 
  636.     #if kCompilingForWSAPI
  637.     WSAPI_CommandPBPtr commandPtr
  638.     #else
  639.     void
  640.     #endif
  641.     )
  642. {
  643.     #if kCompilingForWSAPI
  644.     
  645.     CustomPeriodicTask ();
  646.     
  647.     /* reset sleep time */
  648.     commandPtr->param.idle.ticksToSleep = kSleepTimeForPeriodicTask;
  649.     
  650.     #else
  651.     
  652.     
  653.     #endif
  654. } /* ProcessPeriodicTask */
  655.  
  656. #endif /* kCompileWithPeriodicTask */
  657.  
  658.  
  659. /**  Deferred Tasks (low-priority) thread  **/
  660. #pragma mark -
  661. #if kCompileWithDeferredTask
  662.  
  663. /*  */
  664. pascal void *
  665. threadDeferredTaskThread ( void *threadParam )
  666. {
  667.     ThreadID    currentThread;
  668.     
  669.     #if kCompileWithThreadsOptional
  670.     my_assert ( gHasThreadMgr, "\pthreadDeferredTaskThread: Thread Manager not available" );
  671.     #endif
  672.     
  673.     /* continually call CustomDeferredTask until it is time to quit */
  674.     while ( !gThreadQuit )
  675.     {
  676.         CustomDeferredTask ();
  677.         ProcessGiveTime ( nil, false, NULL );
  678.     }
  679.     
  680.     /* Find the ID of current thread and use DisposeThread to dispose of it so
  681.         that my custom thread termination procedure will be used to recycle
  682.         this thread's allocation for the thread pool. */
  683.     GetCurrentThread ( ¤tThread );
  684.     DisposeThread ( currentThread, (void *)noErr, true );
  685.     
  686.     /* This line below is actually irrelevant, since the DisposeThread call above
  687.         will result in the immediate termination of this thread.
  688.         I keep it in because a return result is needed for the compiler not to
  689.         issue a warning (and I have the "treat all warnings as errors" flag set
  690.         in my compiler, like every programmer should). */
  691.     return (void *)noErr;
  692. } /* threadDeferredTaskThread */
  693.  
  694. #endif /* kCompileWithDeferredTask */
  695.  
  696.  
  697. /**  SLEEPING THREADS  **/
  698. #pragma mark -
  699. #if kStartupThreadsPreallocate > nil
  700. /* this section takes care of dealing with putting threads to sleep, waking
  701.     them up, and keeping track of the sleeping threads.
  702.     It only really applies to situations when resources are tied up and a
  703.     thread can't proceed until some resources become available. In particular,
  704.     when allocating new threads from the pool of preallocated threads, if the
  705.     pool contains no unused threads, the thread attempting to allocate can go
  706.     to sleep until some other thread quits resulting in a thread becoming
  707.     available.
  708.     If you want to be able to put a specific thread to sleep and later wake it
  709.     up under specific circumstances (where you don't want to rely on the queue
  710.     to wake it up because it might be woken too early or too late), you will
  711.     have to handle the sleeping and waking on your own (don't touch the
  712.     sleeping threads queue implemented in this module.
  713.     I've implemented a queue (vThreadSleepers) to track the sleeping threads;
  714.     it stores the ThreadIDs which can then be used to wake up the threads on
  715.     a FIFO (first in first out) basis.
  716.     The position for where to add threads to the queue (as they are put to
  717.     sleep) is tracked in vThreadSleeperAddHere.
  718.     The position for where the next thread to wake is in the queue is
  719.     tracked in vThreadSleeperHead.
  720.     The first item in the vThreadSleepers array is identified as 0 (zero),
  721.     and the last is identified by kStartupThreadsPreallocate - 1. */
  722.  
  723. /*  */
  724. static void
  725. threadSleepersInit ( void )
  726. {
  727.     short    counter;
  728.     
  729.     #if kCompileWithThreadsOptional
  730.     my_assert ( gHasThreadMgr, "\pthreadSleepersInit: Thread Manager not available" );
  731.     #endif
  732.     
  733.     for ( counter = 0; counter < kStartupThreadsPreallocate; counter++ )
  734.     {
  735.         vThreadSleepers[counter] = nil;
  736.     }
  737.     vThreadSleeperHead    = nil;
  738.     vThreadSleeperAddHere = nil;
  739.     vThreadSleepersTotal  = nil;
  740. } /* threadSleepersInit */
  741.  
  742.  
  743. /*  */
  744. p_export OSErr
  745. ThreadSleep ( ThreadID theThread )
  746. {
  747.     OSErr    theErr;
  748.     
  749.     #if kCompileWithThreadsOptional
  750.     my_assert ( gHasThreadMgr, "\pThreadSleep: Thread Manager not available" );
  751.     #endif
  752.     my_assert ( theThread != nil, "\pThreadSleep: theThread is nil" );
  753.     
  754.     if ( vThreadSleepersTotal == kStartupThreadsPreallocate )
  755.     {
  756.         /* cannot put all of the application threads to sleep, at least one
  757.             must be running! */
  758.         theErr = true; /* ••• this should be a more meaningful error value */
  759.     }
  760.     else
  761.     {
  762.         my_assert ( vThreadSleepers[vThreadSleeperAddHere] == nil,
  763.             "\pThreadSleep: the AddHere spot in the vThreadSleepers queue is not nil" );
  764.         
  765.         if ( theThread == vThreadMain )
  766.         {
  767.             /* we're putting the main thread to sleep */
  768.             vThreadMainIsAsleep = true;
  769.         }
  770.         
  771.         /* add the threadID to the queue */
  772.         vThreadSleepers[vThreadSleeperAddHere] = theThread;
  773.         /* adjust the index to the next open spot in the queue */
  774.         ++vThreadSleeperAddHere;
  775.         if ( vThreadSleeperAddHere >= kStartupThreadsPreallocate )
  776.         {
  777.             vThreadSleeperAddHere = 0;
  778.         }
  779.         /* we've got one more sleeping thread, now */
  780.         ++vThreadSleepersTotal;
  781.         
  782.         /* this is the call to put the thread to sleep */
  783.         theErr = SetThreadState ( theThread, kStoppedThreadState, nil );
  784.     }
  785.     
  786.     return theErr;
  787. } /* ThreadSleep */
  788.  
  789.  
  790. /* Waking up a thread does not mean that it will immediately start processing,
  791.     it just means that the thread will be back in the set of threads that can
  792.     be yielded to when ThreadYield is called. */
  793. p_export
  794. OSErr
  795. ThreadWakeNext ( void )
  796. {
  797.     OSErr        theErr;
  798.     ThreadID    theThread;
  799.     
  800.     #if kCompileWithThreadsOptional
  801.     my_assert ( gHasThreadMgr, "\pThreadWakeNext: Thread Manager not available" );
  802.     #endif
  803.     
  804.     /* extract the threadID from the queue */
  805.     theThread = vThreadSleepers[vThreadSleeperHead];
  806.     
  807.     if ( theThread == nil )
  808.     {
  809.         /* no sleeping threads */
  810.         theErr = true; /* ••• need better error value here */
  811.     }
  812.     else
  813.     {
  814.         if ( theThread == vThreadMain )
  815.         {
  816.             /* we're waking up the main thread */
  817.             vThreadMainIsAsleep = false;
  818.         }
  819.         
  820.         /* adjust the index of the head of the queue to the next in line */
  821.         vThreadSleepers[vThreadSleeperHead] = nil;
  822.         ++vThreadSleeperHead;
  823.         if ( vThreadSleeperHead >= kStartupThreadsPreallocate )
  824.         {
  825.             vThreadSleeperHead = nil;
  826.         }
  827.         /* we've got one less sleeping thread, now */
  828.         --vThreadSleepersTotal;
  829.         
  830.         /* this is the call to wake up the thread */
  831.         theErr = SetThreadState ( theThread, kReadyThreadState, nil );
  832.     }
  833.     
  834.     return theErr;
  835. } /* ThreadWakeNext */
  836.  
  837.  
  838. /*  */
  839. void
  840. ThreadWakeAll ( void )
  841. {
  842.     #if kCompileWithThreadsOptional
  843.     my_assert ( gHasThreadMgr, "\pThreadWakeAll: Thread Manager not available" );
  844.     #endif
  845.     
  846.     while ( vThreadSleepersTotal > nil )
  847.     {
  848.         ThreadWakeNext ();
  849.     }
  850. } /* ThreadWakeAll */
  851.  
  852. #endif /* kStartupThreadsPreallocate > nil */
  853.  
  854.  
  855. /*****  EOF  *****/
  856.